package cc.gpai.data_stru.bag;

import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import cc.gpai.data_stru.bag.impl.BagEntry;

public abstract class ListBag<E> extends AbstractBag<E> {
	final List<BagEntry<E>> list = createList();

	protected abstract List<BagEntry<E>> createList();

	@Override
	public boolean add(E e) {
		BagEntry<E> entry = getEntry(e);
		if (entry != null) {
			entry.getValue().incrementAndGet();
		} else {
			list.add(new BagEntry<E>(e));
		}
		return true;
	}

	@Override
	public boolean remove(Object o) {
		int i = indexOf(o);
		if (i < 0) {
			return false;
		}
		BagEntry<E> entry = list.get(i);
		if (entry.getValue().get() > 1) {
			entry.getValue().decrementAndGet();
		} else {
			list.remove(i);
		}
		return true;
	}

	protected int indexOf(Object o) {
		ListIterator<BagEntry<E>> e = list.listIterator();
		if (o == null) {
			while (e.hasNext())
				if (e.next().getKey() == null)
					return e.previousIndex();
		} else {
			while (e.hasNext())
				if (o.equals(e.next().getKey()))
					return e.previousIndex();
		}
		return -1;
	}

	@Override
	public long modCount(E e, long count) {
		int i = indexOf(e);
		if (i < 0) {
			if (count <= 0) {
				return 0;
			} else {
				list.add(new BagEntry<E>(e));
				return count;
			}
		} else {
			long l = list.get(i).getValue().addAndGet(count);
			if (l <= 0) {
				list.remove(i);
				return 0;
			}
			return l;
		}
	}

	@Override
	public void setCount(E e, long count) {
		BagEntry<E> entry = getEntry(e);
		if (entry == null) {
			list.add(new BagEntry<E>(e, count));
		} else {
			entry.getValue().set(count);
		}
	}

	@Override
	public boolean contains(Object o) {
		return getEntry(o) != null;
	}

	protected BagEntry<E> getEntry(Object o) {
		for (BagEntry<E> entry : list) {
			if (entry.getKey().equals(o)) {
				return entry;
			}
		}
		return null;
	}

	// @Override
	// public long getCount(Object obj) {
	// BagEntry<E> entry = getEntry(obj);
	// if (entry == null) {
	// return 0;
	// } else {
	// Number n = entry.getValue();
	// if(n==null){
	// return 0;
	// }
	// return n.longValue();
	// }
	// }

	@Override
	public Set<E> uniqueSet() {
		return new AbstractSet<E>() {

			@Override
			public boolean add(E e) {
				return ListBag.this.add(e);
			}

			@Override
			public int size() {
				return list.size();
			}

			@Override
			public Iterator<E> iterator() {
				return new MyListIterator<E>(list);
			}
		};
	}

	@Override
    public
	Set<Entry<E, AtomicLong>> entrySet() {
		return new AbstractSet<Entry<E, AtomicLong>>() {

			@Override
			public int size() {
				return list.size();
			}

			@Override
			public Iterator<Entry<E, AtomicLong>> iterator() {
				final Iterator<BagEntry<E>> itr = list.iterator();
				return new Iterator<Entry<E, AtomicLong>>() {

					@Override
					public boolean hasNext() {
						return itr.hasNext();
					}

					@Override
					public Entry<E, AtomicLong> next() {
						return itr.next();
					}

					@Override
					public void remove() {
						itr.remove();
					}
				};
			}
		};
	}

}

class MyListIterator<E> implements Iterator<E> {

	final Iterator<BagEntry<E>> itr;

	public MyListIterator(List<BagEntry<E>> list) {
		super();
		itr = list.iterator();
	}

	public boolean hasNext() {
		return itr.hasNext();
	}

	public E next() {
		return itr.next().getKey();
	}

	public void remove() {
		itr.remove();
	}
}